﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 需要挂载在相机上
/// </summary>
public class VIPCamera : MonoBehaviour {

    /// <summary>
    /// 鼠标选中的目标点，或者是其他给的目标点
    /// </summary>
    public Transform Target;

    [SerializeField]
    private float dragSensitivity = 20f;

    /// <summary>
    /// 是否在视图内
    /// </summary>
    private bool isSceneView = true;
    private TargetRotate mouseLimit;

    /// <summary>
    /// 主要的相机
    /// </summary>
    [SerializeField]
    private Camera mainCamera;

    /// <summary>
    /// 是否进入旋转
    /// </summary>
    private bool isRotate;

    /// <summary>
    /// 是否进入拖拽移动
    /// </summary>
    private bool isDrag;

    /// <summary>
    /// 相机参照点
    /// </summary>
    private Transform point;

    /// <summary>
    /// 相机到point点的距离
    /// </summary>
    private float distance;

    /// <summary>
    /// 是否开启注视
    /// </summary>
    private bool isOpenLookAt;

    /// <summary>
    /// 鼠标上一帧的位置
    /// </summary>
    private Vector3 lastMousePosition;
    private bool lockInput;

    /// <summary>
    /// 相机旋转参数
    /// </summary>
    private float x = 0.0f;
    private float y = 0.0f;

    protected virtual void Start()
    {
        if (null == mainCamera)
        {
            mainCamera = Camera.main;
        }
        GameObject go = new GameObject("point");
        point = go.transform;
        mouseLimit = mainCamera.gameObject.GetComponent<TargetRotate>();
        if (mouseLimit == null)
        {
            mouseLimit = mainCamera.gameObject.AddComponent<TargetRotate>();
        }
        UnlockInput();
        mouseLimit.enabled = false;
    }

    public void LockInput()
    {
        lockInput = true;
    }

    private void Enable()
    {
        UnlockInput();
    }

    private void DisEnable()
    {
        LockInput();
        mainCamera.enabled = false;
    }

    private void Update()
    {
        HandleInput();
        UpdateLerpHandle();
    }

    private void HandleInput()
    {
        if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2))
        {
            mouseLimit.enabled = false;
            isRotate = false;
            // isOpenLookAt = false;
            return;
        }

        if (lockInput)
        {
            return;
        }

        if (Input.GetKeyDown(KeyCode.F))
        {
            if (null != Target) {
                Focus(Target);
            }
        }

        bool drag = Input.GetMouseButton(2);
        bool rotate = Input.GetKey(KeyCode.AltGr) || Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
        if (drag != isDrag)
        {
            isDrag = drag;
            if (isDrag)
            {
                isRotate = false;
            }
        }
        else
        {
            if (rotate != isRotate)
            {
                isRotate = rotate;
            }
        }

        bool isLocked = isRotate || drag;

        //如果不在场景视图内
        if (!isSceneView)
        {
            return;
        }

        if (Input.GetMouseButtonDown(1))
        {

            y = mainCamera.transform.localEulerAngles.x;
            x = mainCamera.transform.localEulerAngles.y;
            isOpenLookAt = false;
        }

        if (Input.GetMouseButton(1) && !rotate)//自身旋转
        {
            x += Input.GetAxis("Mouse X") * 250f * 0.02f;
            y -= Input.GetAxis("Mouse Y") * 120f * 0.02f;

            y = TargetRotate.ClampAngle(y, -360f, 360f);

            Quaternion rotation = Quaternion.Euler(y, x, 0);

            mainCamera.transform.rotation = rotation;
            point.position = mainCamera.transform.position + mainCamera.transform.forward * mouseLimit.distance;
        }

        #region 处理相机的靠近拉远
        float mouseWheel = Input.GetAxis("Mouse ScrollWheel");

        //按住滚轮键的靠近拉远
        if (mouseWheel != 0 && !rotate)
        {
            Vector3 distance = mainCamera.transform.forward * mouseWheel * 10f;
            mainCamera.transform.position += distance;
            if (mouseWheel < 0)
                mouseLimit.distance += distance.magnitude;
            else mouseLimit.distance += -distance.magnitude;
        }

        //放大缩小模式//按住ALT加鼠标滑动的放大拉远
        if (Input.GetMouseButton(1) && rotate)
        {
            float x = Input.GetAxis("Mouse X") * 250f * 0.02f;
            Vector3 distance = mainCamera.transform.forward * x * 0.05f;
            mainCamera.transform.position += distance;
            if (x < 0)
                mouseLimit.distance += distance.magnitude;
            else mouseLimit.distance += -distance.magnitude;
        }
        #endregion

        if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(2))
        {
            lastMousePosition = Input.mousePosition;
            if (isRotate)
            {
                mouseLimit.enabled = true;
            }
        }

        if (isLocked)
        {
            if (isDrag && !isRotate)
            {
                Drag();
            }
        }
    }

    public void UnlockInput()
    {
        lockInput = false;
        if (mouseLimit != null)
        {
            point.position = mainCamera.transform.position + mainCamera.transform.forward * mouseLimit.distance;
            mouseLimit.Target = point;
            mouseLimit.SyncAngles();
        }
    }

    /// <summary>
    /// 拖拽
    /// </summary>
    private void Drag()
    {
        Vector3 delta = lastMousePosition - Input.mousePosition;
        delta = delta / Mathf.Sqrt(mainCamera.pixelHeight * mainCamera.pixelHeight + mainCamera.pixelWidth * mainCamera.pixelWidth);
        delta *= dragSensitivity;
        delta = mainCamera.cameraToWorldMatrix.MultiplyVector(delta);
        mainCamera.transform.position += delta;
        point.position += delta;
        lastMousePosition = Input.mousePosition;
    }

    /// <summary>
    /// 处理插值事务
    /// </summary>
    private void UpdateLerpHandle()
    {
        if (isOpenLookAt)
        {
            Vector3 targetToCaemraVector3 = (mainCamera.transform.position - point.position).normalized * distance;
            Vector3 cameraDir = -mainCamera.transform.forward * distance;
            Vector3 dir = Vector3.Lerp(targetToCaemraVector3, cameraDir, Time.deltaTime * 15f);
            mainCamera.transform.position = point.position + dir;

            float value = Mathf.Abs(Vector3.Dot(dir.normalized, targetToCaemraVector3.normalized));

            if (Mathf.Abs(value - 1f) < 0.0000001f)
            {
                //Debug.Log("关闭插值");
                isOpenLookAt = false;
            }
        }
    }
    private Bounds CalculateBounds(Transform t)
    {
        Renderer renderer = t.GetComponentInChildren<Renderer>();
        if (renderer)
        {
            Bounds bounds = renderer.bounds;
            if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position)
            {
                bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds);
            }
            CalculateBounds(t, ref bounds);
            if (bounds.extents == Vector3.zero)
            {
                bounds.extents = new Vector3(0.5f, 0.5f, 0.5f);
            }
            return bounds;
        }
        return new Bounds(t.position, new Vector3(0.5f, 0.5f, 0.5f));
    }

    private void CalculateBounds(Transform t, ref Bounds totalBounds)
    {
        foreach (Transform child in t)
        {
            Renderer renderer = child.GetComponent<Renderer>();
            if (renderer)
            {
                Bounds bounds = renderer.bounds;
                if (bounds.size == Vector3.zero && bounds.center != renderer.transform.position)
                {
                    bounds = TransformBounds(renderer.transform.localToWorldMatrix, bounds);
                }
                totalBounds.Encapsulate(bounds.min);
                totalBounds.Encapsulate(bounds.max);
            }

            CalculateBounds(child, ref totalBounds);
        }
    }

    public static Bounds TransformBounds(Matrix4x4 matrix, Bounds bounds)
    {
        var center = matrix.MultiplyPoint(bounds.center);

        // transform the local extents' axes
        var extents = bounds.extents;
        var axisX = matrix.MultiplyVector(new Vector3(extents.x, 0, 0));
        var axisY = matrix.MultiplyVector(new Vector3(0, extents.y, 0));
        var axisZ = matrix.MultiplyVector(new Vector3(0, 0, extents.z));

        // sum their absolute value to get the world extents
        extents.x = Mathf.Abs(axisX.x) + Mathf.Abs(axisY.x) + Mathf.Abs(axisZ.x);
        extents.y = Mathf.Abs(axisX.y) + Mathf.Abs(axisY.y) + Mathf.Abs(axisZ.y);
        extents.z = Mathf.Abs(axisX.z) + Mathf.Abs(axisY.z) + Mathf.Abs(axisZ.z);

        return new Bounds { center = center, extents = extents };
    }

    /// <summary>
    /// 复位
    /// </summary>
    public void Focus(Transform target)
    {
        isOpenLookAt = true;
        Bounds bounds = CalculateBounds(target);
        float fov = mainCamera.fieldOfView * Mathf.Deg2Rad;
        float objSize = Mathf.Max(bounds.extents.y, bounds.extents.x, bounds.extents.z) * 2.0f;
        distance = Mathf.Abs(objSize / Mathf.Sin(fov / 2.0f));
        mouseLimit.distance = distance;
        point.position = bounds.center;
    }

}
